package com.ldj.base.impl;

import com.ldj.base.AbstractHibernateDaoSupport;
import com.ldj.base.BaseDao;
import com.ldj.base.Page;
import com.ldj.base.ReflectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.*;
import org.hibernate.criterion.*;
import org.hibernate.metadata.ClassMetadata;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * All rights Reserved, Designed By 翼虎能源
 * Copyright:    Copyright(C) 2015-2015
 * Company       北京翼虎能源科技有限公司
 *
 * @author 吕东杰
 * @version 1.0
 * @date 2016/5/26 15:45
 * @Description Demo
 */
public class BaseDaoImpl<Entity, PK extends Serializable> extends AbstractHibernateDaoSupport implements BaseDao<Entity, PK> {
    protected SessionFactory sessionFactory;
    protected Class<Entity> entityClass;

    public BaseDaoImpl() {
        this.entityClass = ReflectionUtils.getSuperGenericType(this.getClass());
    }

    public BaseDaoImpl(SessionFactory sessionFactory, Class<Entity> entityClass) {
        this.sessionFactory = sessionFactory;
        this.entityClass = entityClass;
    }

    public SessionFactory getSessionFactory() {
        return this.sessionFactory;
    }

    @Resource
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Serializable save(Entity entity) {
        return this.getSession().save(entity);
    }

    public void update(Entity entity) {
        this.getSession().update(entity);
    }

    public void merge(Entity entity) {
        this.getSession().merge(entity);
    }

    public void refresh(Entity entity) {
        this.getSession().refresh(entity);
    }

    public void delete(Entity entity) {
        this.getSession().delete(entity);
    }

    public void delete(PK id) {
        this.delete(this.findById(id));
    }

    public Entity findById(PK id) {
        return (Entity) this.getSession().get(this.entityClass, id);
    }

    public List<Entity> findByIds(Collection<PK> ids) {
        return this.find(new Criterion[]{Restrictions.in(this.getIdName(), ids)});
    }

    public List<Entity> getAll() {
        return this.find(new Criterion[0]);
    }

    public List<Entity> getAll(String orderByProperty, boolean isAsc) {
        Criteria c = this.createCriteria(new Criterion[0]);
        if(isAsc) {
            c.addOrder(Order.asc(orderByProperty));
        } else {
            c.addOrder(Order.desc(orderByProperty));
        }

        return c.list();
    }

    public List<Entity> findByProperty(String propertyName, Object value) {
        SimpleExpression criterion = Restrictions.eq(propertyName, value);
        return this.find(new Criterion[]{criterion});
    }

    public Entity findUniqueByProperty(String propertyName, Object value) {
        SimpleExpression criterion = Restrictions.eq(propertyName, value);
        return (Entity) this.createCriteria(new Criterion[]{criterion}).uniqueResult();
    }

    public <X> List<X> findByHql(String hql, Object... values) {
        return this.createQuery(hql, values).list();
    }

    public <X> List<X> findByHql(String hql, Map<String, ?> values) {
        return this.createQuery(hql, values).list();
    }

    public <X> X findUniqueByHql(String hql, Object... values) {
        return (X) this.createQuery(hql, values).uniqueResult();
    }

    public <X> X findUniqueByHql(String hql, Map<String, ?> values) {
        return (X) this.createQuery(hql, values).uniqueResult();
    }

    public int batchExecute(String hql, Object... values) {
        return this.createQuery(hql, values).executeUpdate();
    }

    public int batchExecute(String hql, Map<String, ?> values) {
        return this.createQuery(hql, values).executeUpdate();
    }

    public Query createQuery(String queryString, Object... values) {
        Query query = this.getSession().createQuery(queryString);
        if(values != null) {
            for(int i = 0; i < values.length; ++i) {
                query.setParameter(i, values[i]);
            }
        }

        return query;
    }

    public Query createSQLQuery(String queryString, Object... values) {
        SQLQuery query = this.getSession().createSQLQuery(queryString);
        if(values != null) {
            for(int i = 0; i < values.length; ++i) {
                query.setParameter(i, values[i]);
            }
        }

        return query;
    }

    public Query createQuery(String queryString, Map<String, ?> values) {
        Query query = this.getSession().createQuery(queryString);
        if(values != null) {
            query.setProperties(values);
        }

        return query;
    }

    public List<Entity> find(Criterion... criterions) {
        return this.createCriteria(criterions).list();
    }

    public Entity findUnique(Criterion... criterions) {
        return (Entity) this.createCriteria(criterions).uniqueResult();
    }

    public Criteria createCriteria(Criterion... criterions) {
        Criteria criteria = this.getSession().createCriteria(this.entityClass);
        Criterion[] arr$ = criterions;
        int len$ = criterions.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            Criterion c = arr$[i$];
            criteria.add(c);
        }

        return criteria;
    }

    public void initProxyObject(Object proxy) {
        Hibernate.initialize(proxy);
    }

    public void flush() {
        this.getSession().flush();
    }

    public Query distinct(Query query) {
        query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        return query;
    }

    public Criteria distinct(Criteria criteria) {
        criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        return criteria;
    }

    public String getIdName() {
        ClassMetadata meta = this.getSessionFactory().getClassMetadata(this.entityClass);
        return meta.getIdentifierPropertyName();
    }

    public boolean isPropertyUnique(String propertyName, Object newValue, Object oldValue) {
        if(newValue != null && !newValue.equals(oldValue)) {
            Object object = this.findUniqueByProperty(propertyName, newValue);
            return object == null;
        } else {
            return true;
        }
    }

    public void saveOrUpdate(Entity entity) {
        this.getSession().saveOrUpdate(entity);
    }

    public Page findPageByHQL(Page page, String hql, boolean distinct) {
        Query q = this.createQuery(hql, new Object[0]);
        if(distinct) {
            q = this.distinct(q);
        }

        if(page.isAutoCount()) {
            long result = this.countHqlResult(hql, new Object[0]);
            page.setTotalCount(result);
        }

        this.setPageParameterToQuery(q, page);
        List result1 = q.list();
        page.setResult(result1);
        return page;
    }

    protected long countHqlResult(String hql, Object... values) {
        String countHql = this.prepareCountHql(hql);

        try {
            Long e = Long.valueOf(this.findUniqueByHql(countHql, values).toString());
            return e.longValue();
        } catch (Exception var5) {
            throw new RuntimeException("hql can\'t be auto count, hql is:" + countHql, var5);
        }
    }

    protected String prepareCountHql(String orgHql) {
        String fromHql = orgHql;
        int first = orgHql.indexOf("select");

        for(int firstFrom = orgHql.indexOf("from"); fromHql.substring(first + 6, firstFrom).contains("select"); first = 0) {
            fromHql = fromHql.substring(firstFrom + 4);
            firstFrom = fromHql.indexOf("from");
        }

        fromHql = "from " + StringUtils.substringAfter(fromHql, "from");
        fromHql = StringUtils.substringBefore(fromHql, "order by");
        String countHql = "select count(*) " + fromHql;
        return countHql;
    }

    protected Query setPageParameterToQuery(Query q, Page page) {
        q.setFirstResult(page.getFirst() - 1);
        q.setMaxResults(page.getPageSize());
        return q;
    }

    public Page findPageBySQL(Page page, String sql, boolean distinct) {
        Query q = this.createSQLQuery(sql, new Object[0]);
        if(distinct) {
            q = this.distinct(q);
        }

        if(page.isAutoCount()) {
            long result = this.countSqlResult(sql, new Object[0]);
            page.setTotalCount(result);
        }

        this.setPageParameterToQuery(q, page);
        List result1 = q.list();
        page.setResult(result1);
        return page;
    }

    public <X> X findUniqueBySql(String sql, Object... values) {
        return (X) this.createSQLQuery(sql, values).uniqueResult();
    }

    protected long countSqlResult(String sql, Object... values) {
        String countSql = this.prepareCountHql(sql);

        try {
            Long e = Long.valueOf(this.findUniqueBySql(countSql, values).toString());
            return e.longValue();
        } catch (Exception var5) {
            throw new RuntimeException("sql can\'t be auto count, sql is:" + countSql, var5);
        }
    }

    public Page findPageByHQL(Page page, String hqlQuery, String hqlCount, boolean distinct) {
        Query q = this.createQuery(hqlQuery, new Object[0]);
        if(distinct) {
            q = this.distinct(q);
        }

        long totalCount = ((Long)this.findUniqueByHql(hqlCount, new Object[0])).longValue();
        page.setTotalCount(totalCount);
        this.setPageParameterToQuery(q, page);
        List result = q.list();
        page.setResult(result);
        return page;
    }

    public Page findPageBySQL(Page page, String sqlQuery, String sqlCount, boolean distinct) {
        Query q = this.createSQLQuery(sqlQuery, new Object[0]);
        if(distinct) {
            q = this.distinct(q);
        }

        long totalCount = ((Long)this.findUniqueBySql(sqlCount, new Object[0])).longValue();
        page.setTotalCount(totalCount);
        this.setPageParameterToQuery(q, page);
        List result = q.list();
        page.setResult(result);
        return page;
    }
}
